//////////////////////////////////////
// Fast Container Library           //
// MIT License - Copyright (c) 2012 //
//////////////////////////////////////

#pragma once

#include "memory.h"

#ifdef _MSC_VER
	#pragma warning ( push )
	#pragma warning ( disable : 4100 )
#endif

FC_NAMESPACE_BEGIN


class virtual_allocator
{
public:
	virtual_allocator() {}
	virtual ~virtual_allocator() {}

	virtual void* allocate( size_t n, size_t alignment = 0 )
	{
		return fc::allocate(n);
	}

	virtual void deallocate( void* ptr, size_t /*size*/ )
	{
		fc::deallocate(ptr);
	}

	virtual bool operator ==( const virtual_allocator& ) { return true; }
	virtual bool operator !=( const virtual_allocator& ) { return false; }

	virtual bool is_equal( const virtual_allocator& ) { return true; }

	template <class T>
	T* allocate_array( size_t n )
	{
		if( n == 0 )
			return (T*)0;

		//allocate enough room for header
		size_t alignment = FC_ALIGNOF(T);
		T* p = (T*)( allocate(sizeof(T) * n + FC_DEFAULT_ALIGN, alignment) + alignment );
		*((size_t*)p - 1) = n;

		fc::construct_n(p, n);
		return p;
	}

	template <class T>
	void deallocate_array( T* ptr )
	{
		if( !ptr )
			return;

		//rewind to header
		size_t n = *(((size_t*)ptr) - 1);
		fc::destroy_n(ptr, n);

		deallocate( ptr, n * sizeof(T) + sizeof(size_t) );
	}

	//virtual allocator* clone() const { return this; }
};



// default allocation model
namespace internal
{
	template <class Proxy>
	struct virtual_allocator_holder
	{
		static virtual_allocator default_allocator_instance;
		static virtual_allocator* default_allocator_ptr;
	};

	template <class T> virtual_allocator virtual_allocator_holder<T>::default_allocator_instance = virtual_allocator();
	template <class T> virtual_allocator* virtual_allocator_holder<T>::default_allocator_ptr =
		&virtual_allocator_holder<T>::default_allocator_instance;
}

inline virtual_allocator* get_default_allocator()
	{
		return internal::virtual_allocator_holder<void>::default_allocator_ptr;
	}

inline void set_default_allocator( virtual_allocator* alloc )
	{
		if( alloc )
			internal::virtual_allocator_holder<void>::default_allocator_ptr = alloc;
	}


class polymorphic_allocator
{
public:
	polymorphic_allocator() : _alloc(get_default_allocator()) {}
	polymorphic_allocator( virtual_allocator* p ) : _alloc(p) {}

	virtual_allocator* get_virtual_allocator() const { return _alloc; }
	void set_virtual_allocator( virtual_allocator* alloc ) { _alloc = alloc; }

	void* allocate( size_t n, size_t alignment = 0 )
	{
		return _alloc->allocate( n, alignment );
	}

	void deallocate( void* ptr, size_t size = 0 )
	{
		_alloc->deallocate( ptr, size );
	}

	bool operator ==( const polymorphic_allocator& ) { return true; }

protected:
	virtual_allocator* _alloc;
};



FC_NAMESPACE_END

#ifdef _MSC_VER
	#pragma warning ( pop )
#endif

